package com.example.springcode.beans.factory;

import cn.hutool.core.bean.BeanUtil;
import com.example.springcode.beans.*;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: chenxiuxiang
 * @Date: 2024-03-04 18:06
 * @Description:
 */
public class BeanFactory {
    /**
     * Bean定义集合
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    /**
     * 单例对象集合 存储的是<Bean名称, 对象>的集合
     */
    private final Map<String, Object> singletonObjects = new HashMap<>();
    /**
     * Bean后置处理器集合
     */
    private final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    /**
     * 可销毁的Bean对象
     */
    private final Map<String, Object> disposableBeans = new HashMap<>();
    /**
     * 由FactoryBean创建的单例对象
     */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();

    private static final Object NULL_OBJECT = new Object();

    /**
     * 获取Bean 无构造函数
     * @param name Bean名称
     * @return Bean对象
     */
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null);
    }

    /**
     * 获取Bean 有构造函数
     * @param name Bean名称
     * @param args 属性
     * @return Bean对象
     */
    public Object getBean(String name, Object... args) {
        return doGetBean(name, args);
    }

    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return (T) getBean(name);
    }

    /**
     * 获取Bean对象
     * @param name Bean名称
     * @param args 属性
     * @param <T>  类型
     * @return Bean对象
     */
    private <T> T doGetBean(final String name, final Object[] args) {
        System.out.println("获取Bean: " + name);
        // 先从单例Bean集合中获取
        Object sharedInstance = getSingleton(name);
        // 如果获取到了则说明之前已经注册了 则从Bean实例中获取
        if (sharedInstance != null) {
            return (T) getObjectForBeanInstance(sharedInstance, name);
        }

        // 如果没有，则说明之前还没注册
        // 则需要首次创建Bean
        BeanDefinition beanDefinition = getBeanDefinition(name);
        Object bean = createBean(name, beanDefinition, args);

        // 创建完成后 在从Bean实例中获取
        return (T) getObjectForBeanInstance(bean, name);
    }

    /**
     * 通过FactoryBean 扩展Bean实例
     * 此时Bean已经创建完成，可以通过实现此接口代理/扩展Bean
     * @param beanInstance Bean实例
     * @param beanName     Bean名称
     * @return Bean实例
     */
    private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
        System.out.println("通过FactoryBean生成Bean: " + beanName);

        System.out.println(beanInstance instanceof FactoryBean);
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }

        // 先从缓存中获取
        Object object = getCacheObjectForFactoryBean(beanName);

        // 如果没有再创建
        if (object == null) {
            FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
            object = getObjectFromFactoryBean(factoryBean, beanName);
        }

        System.out.println("Bean已经被重置了！: " + beanName);
        return object;
    }

    /**
     * 注册Bean定义
     * @param name           Bean名称
     * @param beanDefinition Bean定义
     */
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
        System.out.println("注册Bean定义: " + name);
        beanDefinitionMap.put(name, beanDefinition);
    }

    /**
     * 获取Bean定义
     * @param beanName Bean名称
     * @return Bean定义
     * @throws BeansException Bean异常
     */
    public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
        // 从Bean定义集合中获取
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 如果不存在 则提示错误
        if (beanDefinition == null) {
            throw new BeansException(beanName + " 未定义");
        }

        return beanDefinition;
    }

    /**
     * 获取单例Bean
     * @param beanName Bean名称
     * @return 单例Bean
     */
    public Object getSingleton(String beanName) {
        System.out.println("获取单例Bean: " + beanName);
        return singletonObjects.get(beanName);
    }

    /**
     * 创建Bean对象
     * @param beanName       Bean名称
     * @param beanDefinition Bean定义
     * @param args           构造函数参数
     * @return Bean对象
     * @throws BeansException Bean异常
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        System.out.println("创建Bean: " + beanName);
        Object bean;

        try {
            // 1. 创建Bean实例
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 2. 给Bean对象填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
            // 3. 初始化Bean对象
            bean = initializeBean(beanName, bean);
        } catch (Exception e) {
            throw new BeansException("初始化Bean失败:", e);
        }

        // 4. 注册销毁Bean方法
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        // 5. 创建完成后增加到单例Bean集合中
        if (beanDefinition.isSingleton()) {
            registerSingleton(beanName, bean);
        }
        return bean;
    }

    /**
     * 创建Bean实例
     * @param beanDefinition Bean定义
     * @param beanName       Bean名称
     * @param args           构造函数参数
     * @return Bean对象
     */
    private Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        System.out.println("1. 创建Bean实例: " + beanName);
        Constructor<?> constructorToUse = null;

        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor<?> ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        // 使用策略创建Bean实例
        return instantiate(beanDefinition, constructorToUse, args);
    }

    /**
     * 实例化Bean
     * @param beanDefinition Bean定义
     * @param ctor           构造函数
     * @param args           参数
     * @return Bean对象
     * @throws BeansException Bean异常
     */
    private Object instantiate(BeanDefinition beanDefinition, Constructor<?> ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctor) {
            return enhancer.create();
        }
        return enhancer.create(ctor.getParameterTypes(), args);
    }

    /**
     * 增加Bean对象属性
     * @param beanName       Bean名称
     * @param bean           Bean对象
     * @param beanDefinition Bean定义
     */
    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            // 从Bean定义中获取Bean属性
            System.out.println("2. 给Bean对象填充属性: " + beanName);
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                // 属性是Bean对象
                if (value instanceof BeanReference) {
                    BeanReference beanReference = (BeanReference) value;
                    // 循环填充Bean属性
                    value = getBean(beanReference.getBeanName());
                }
                // 填充Bean属性
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("设置" + beanName + "属性错误: " + e);
        }
    }

    /**
     * 初始化Bean对象
     * @param beanName Bean名称
     * @param bean     Bean对象
     * @return Bean对象
     */
    private Object initializeBean(String beanName, Object bean) {
        System.out.println("3. 初始化Bean: " + beanName);
        // 1. 激活感知器方法
        invokeAwareMethod(bean);

        // 2.执行初始化前的 前置处理器
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        try {
            // 3.执行初始化方法
            invokeInitMethods(wrappedBean);
        } catch (Exception e) {
            throw new BeansException("执行 " + beanName + " 初始化方法失败: ", e);
        }

        // 4.执行初始化后的 前置处理器
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

        return wrappedBean;
    }

    /**
     * 激活感知器方法
     * @param bean Bean对象
     */
    private void invokeAwareMethod(Object bean) {
        System.out.println("3.1 激活感知器方法");
        if (bean instanceof Aware) {
            // 如果实现了Bean工厂感知器
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            // 还可以激活其他的感知器
        }
    }

    /**
     * 执行Bean对象初始化前的前置处理器
     * @param exisingBean Bean对象
     * @param beanName    Bean名称
     * @return Bean对象
     * @throws BeansException Bean异常
     */
    private Object applyBeanPostProcessorsBeforeInitialization(Object exisingBean, String beanName) throws BeansException {
        System.out.println("3.2 初始化前 执行前置处理器");
        Object result = exisingBean;

        // 依次遍历执行
        for (BeanPostProcessor processor : getBeanPostProcessorList()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);

            if (null == current) {
                return result;
            }

            result = current;
        }

        return result;
    }

    /**
     * 执行初始化方法
     * @param bean Bean对象
     * @throws Exception 异常
     */
    private void invokeInitMethods(Object bean) throws Exception {
        System.out.println("3.3 执行初始化方法");
        // 如果实现了初始化Bean接口 则执行
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    /**
     * 执行Bean对象初始化后的前置处理器
     * @param exisingBean Bean对象
     * @param beanName    Bean名称
     * @return Bean对象
     * @throws BeansException Bean异常
     */
    public Object applyBeanPostProcessorsAfterInitialization(Object exisingBean, String beanName) throws BeansException {
        System.out.println("3.4 初始化后 执行前置处理器");
        Object result = exisingBean;

        // 依次遍历执行
        for (BeanPostProcessor processor : getBeanPostProcessorList()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);

            if (null == current) {
                return result;
            }

            result = current;
        }

        return result;
    }

    /**
     * 注册销毁Bean方法
     * @param beanName       Bean名称
     * @param bean           Bean对象
     * @param beanDefinition Bean定义
     */
    private void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        // 非单例模式的Bean对象不执行销毁方法
        if (!beanDefinition.isSingleton()) {
            return;
        }

        if (bean instanceof DisposableBean) {
            registerDisposableBean(beanName, bean);
        }
    }

    /**
     * 注册可销毁Bean
     * @param beanName Bean名称
     * @param bean     Bean对象
     */
    public void registerDisposableBean(String beanName, Object bean) {
        System.out.println("注册可销毁Bean: " + beanName);
        disposableBeans.put(beanName, bean);
    }

    /**
     * 注册单例Bean
     * @param beanName        Bean名称
     * @param singletonObject 单例Bean对象
     */
    public void registerSingleton(String beanName, Object singletonObject) {
        System.out.println("注册单例Bean: " + beanName);
        singletonObjects.put(beanName, singletonObject);
    }

    /**
     * 销毁单例Bean
     */
    public void destroySingletons() {
        System.out.println("销毁单例Bean");
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            Object disposableBean = disposableBeans.remove(beanName);

            try {
                ((DisposableBean) disposableBean).destroy();
            } catch (Exception e) {
                throw new BeansException(beanName + " 的销毁方法异常", e);
            }
        }
    }

    /**
     * 注册Bean前置处理器
     * @param beanPostProcessor Bean前置处理器
     */
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        System.out.println("注册Bean前置处理器");
        this.beanPostProcessorList.remove(beanPostProcessor);
        this.beanPostProcessorList.add(beanPostProcessor);
    }

    /**
     * 获取所有Bean前置处理器
     * @return 所有Bean前置处理器
     */
    public List<BeanPostProcessor> getBeanPostProcessorList() {
        return this.beanPostProcessorList;
    }

    /**
     * 从缓存中获取FactoryBean
     * @param beanName Bean名称
     * @return Bean实例
     */
    private Object getCacheObjectForFactoryBean(String beanName) {
        Object object = this.factoryBeanObjectCache.get(beanName);

        return (object != NULL_OBJECT ? object : null);
    }

    /**
     * 从FactoryBean中重新获取Bean对象
     * @param factoryBean FactoryBean
     * @param beanName    Bean名称
     * @return Bean对象
     */
    private Object getObjectFromFactoryBean(FactoryBean<?> factoryBean, String beanName) {
        System.out.println("从FactoryBean中获取Bean: " + beanName);

        // 如果是单例Bean先从缓存中获取
        if (factoryBean.isSingleton()) {
            Object object = this.factoryBeanObjectCache.get(beanName);

            if (object == null) {
                object = doGetObjectFromFactoryBean(factoryBean, beanName);
                this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
            }

            return (object != NULL_OBJECT ? object : null);
        } else {
            return doGetObjectFromFactoryBean(factoryBean, beanName);
        }
    }

    /**
     * 从FactoryBean中重新获取Bean对象
     * @param factoryBean FactoryBean
     * @param beanName    Bean名称
     * @return Bean对象
     */
    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factoryBean, final String beanName) {
        try {
            // 执行FactoryBean的方法
            System.out.println("执行FactoryBean的方法");
            return factoryBean.getObject();
        } catch (Exception e) {
            throw new BeansException("Factory创建Bean: " + beanName + "异常", e);
        }
    }

    /**
     * 获取指定类型的Bean定义
     * @param type 类型
     * @param <T>  类型
     * @return Bean定义
     * @throws BeansException Bean异常
     */
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        Map<String, T> result = new HashMap<>();
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            Class<?> beanClass = beanDefinition.getBeanClass();
            // 是否实现了某个接口
            if (type.isAssignableFrom(beanClass)) {
                result.put(beanName, (T) getBean(beanName));
            }
        });

        return result;
    }
}
